package com.model.objects;

import java.util.Iterator;
import java.util.List;
import java.util.Random;

import com.model.logic.Field;
import com.model.logic.Location;
import com.model.logic.Randomizer;


/**
 * A simple model of a crocodile.
 * crocodiles age, move, breed, and die.
 */
public class Crocodile extends Animal
{
    // Characteristics shared by all crocodiles (class variables).

    // The age at which a crocodile can start to breed.
    private static int breeding_age = 12;
    // The age to which a crocodile can live.
    private static int max_age = 40;
    // The likelihood of a crocodile breeding.
    private static double breeding_probability = 0.06;
    // The maximum number of births.
    private static int max_litter_size = 3;
    // The food value of rabbits
    private static final int RABBIT_FOOD_VALUE = 7;
    // The food value of foxes
    private static final int FOX_FOOD_VALUE = 12;
    
    // A shared random number generator to control breeding.
    private static final Random rand = Randomizer.getRandom();
    // The food level of the crocodile
    private int foodLevel;
    // The crocodile's age.
    private int age;

    /**
     * Create a new crocodile. A crocodile may be created with age
     * zero (a new born) or with a random age.
     * 
     * @param randomAge If true, the crocodile will have a random age.
     * @param field The field currently occupied.
     * @param location The location within the field.
     */
    public Crocodile(boolean randomAge, Field field, Location location)
    {
        super(field, location);
        if(randomAge) {
            age = rand.nextInt(max_age);
            foodLevel = rand.nextInt(FOX_FOOD_VALUE);
        }
        else {
            age = 0;
            foodLevel = FOX_FOOD_VALUE;
        }
    }
    
    /**
     * This is what the crocodile does most of the time - it runs 
     * around. Sometimes it will breed or die of old age.
     * @param newcrocodiles A list to return newly born crocodiles.
     */
    public void act(List<Actor> newCrocodile)
    {
        incrementAge();
        incrementHunger();
        if(isAlive()) {
            giveBirth(newCrocodile);            
            // Move towards a source of food if found.
            Location newLocation = findFood();
            if(newLocation == null) { 
                // No food found - try to move to a free location.
                newLocation = getField().randomAdjacentLocation(getLocation());
            }
            // See if it was possible to move.
            if(newLocation != null) {
                setLocation(newLocation);
            }
            else {
                // Overcrowding.
                setDead();
            }
        }
    }
    
    /**
     * Look for rabbits or foxes adjacent to the current location.
     * Only the first live animal is eaten.
     * @return Where food was found, or null if it wasn't.
     */
    private Location findFood()
    {
        Field field = getField();
        List<Location> adjacent = field.adjacentLocations(getLocation());
        Iterator<Location> it = adjacent.iterator();
        while(it.hasNext()) {
            Location where = it.next();
            Object animal = field.getObjectAt(where);
            if(animal instanceof Rabbit) {
                Rabbit rabbit = (Rabbit) animal;
                if(rabbit.isAlive()) { 
                    rabbit.setDead();
                    if(foodLevel <= FOX_FOOD_VALUE) {
                    foodLevel += RABBIT_FOOD_VALUE;
                    }
                    else {
                    foodLevel = FOX_FOOD_VALUE;	
                    }
                    return where;
                }
            }
            else if(animal instanceof Fox) {
                Fox fox = (Fox) animal;
                if(fox.isAlive()) { 
                    fox.setDead();
                    foodLevel = FOX_FOOD_VALUE;
                    return where;
                }
            }
        }
        return null;
    }

    /**
     * Increase the age.
     * This could result in the crocodile's death.
     */
    private void incrementAge()
    {
        age++;
        if(age > max_age) {
            setDead();
        }
    }
    
    /**
     * Decrease the food value to replicate the increase of hunger
     * This could result in the crocodile's death
     */
    private void incrementHunger()
    {
        foodLevel--;
        if(foodLevel <= 0) {
            setDead();
        }
    }
    
    /**
     * Check whether or not this crocodile is to give birth at this step.
     * New births will be made into free adjacent locations.
     * @param newCrocodile A list to return newly born crocodiles.
     */
    private void giveBirth(List<Actor> newCrocodile)
    {
        // New crocodiles are born into adjacent locations.
        // Get a list of adjacent free locations.
        Field field = getField();
        List<Location> free = field.getFreeAdjacentLocations(getLocation());
        int births = breed();
        for(int b = 0; b < births && free.size() > 0; b++) {
            Location loc = free.remove(0);
            Crocodile young = new Crocodile(false, field, loc);
            newCrocodile.add(young);
        }
    }
        
    /**
     * Generate a number representing the number of births,
     * if it can breed.
     * @return The number of births (may be zero).
     */
    private int breed()
    {
        int births = 0;
        if(canBreed() && rand.nextDouble() <= breeding_probability) {
            births = rand.nextInt(max_litter_size) + 1;
        }
        return births;
    }

    /**
     * A crocodile can breed if it has reached the breeding age.
     * @return true if the crocodile can breed, false otherwise.
     */
    private boolean canBreed()
    {
        return age >= breeding_age;
    }
    
    /**
     * setter voor breeding_age
     * @param breeding_age
     */
    public static void setBreedingAge(int breeding_age)
    {
    	if (breeding_age >= 0)
    		Crocodile.breeding_age = breeding_age;
    }
    
    /**
     * setter voor max_age
     * @param max_age
     */
    public static void setMaxAge(int max_age)
    {
    	if (max_age >= 1)
    		Crocodile.max_age = max_age;
    }
    
    /**
     * setter voor breeding_probability
     * @param breeding_probability
     */
    public static void setBreedingProbability(double breeding_probability)
    {
    	if (breeding_probability >= 0)
    		Crocodile.breeding_probability = breeding_probability;
    }    
    
    /**
     * setter voor max_litter_size
     * @param max_litter_size
     */
    public static void setMaxLitterSize(int max_litter_size)
    {
    	if (max_litter_size >= 1)
    		Crocodile.max_litter_size = max_litter_size;
    }  
    
    /**
     * getter voor breeding_age
     */
    public static String getBreedingAge()
    {
    	return "currently at " + Integer.toString(breeding_age);
    }
    
    /**
     * getter voor max_age
     */
    public static String getMaxAge()
    {
    	return "currently at " + Integer.toString(max_age);
    }
    
    /**
     * getter voor breeding_probability
     */
    public static String getBreedingProbablity()
    {
    	return "currently at " + Double.toString(breeding_probability);
    }
    
    /**
     * getter voor max_litter_size
     */
    public static String getMaxLitterSize()
    {
    	return "currently at " + Integer.toString(max_litter_size);
    }
    
    /**
     * default settings
     */
    public static void setDefault()
    {
    	breeding_age = 5;
    	max_age = 200;
    	breeding_probability = 0.05;
    	max_litter_size = 3;
    }
}
